//tickettaker.cc

#include "tickettaker.h"
#include "cineplex.h"

TicketTaker::TicketTaker(char* name, Line* line)
{
	myName = name;
	msgList = new MsgList();
	myThread = new Thread(myName);
	msgLock = new Lock(myName);
	sleepCon = new Condition(myName);
	myLine=line;
	status="Idle";
	SizeOfGroupNotInTheater=0;
	myLine->myClerk =(void *) this;
	myLine->myType=1;
}

TicketTaker::~TicketTaker()
{

}

void
TicketTaker::Start()
{

	printf("Starting TicketTaker %s...\n", myName);
	if(getCineplex()->runSchedulers) Scheduler();
}

void
TicketTaker::SendMessage(char* name, int argc, ...)
{
	msgLock->Acquire();
	
	IterList* args = new IterList();
	va_list vali;
	va_start(vali, argc);
	
	for ( int i = 0; i < argc; i++ )        
    {
        args->Append(va_arg ( vali, void* )); 
    }
	va_end(vali);
	
	msgList->Append(name, args);
	sleepCon->Signal(msgLock);
	
	msgLock->Release();
}

void
TicketTaker::SendMessage(char* name, IterList* args)
{
	msgLock->Acquire();
	
	msgList->Append(name, args);
	sleepCon->Signal(msgLock);
	
	msgLock->Release();
}

void
TicketTaker::Scheduler()
{
	bool runScheduler = getCineplex()->runSchedulers;
	do
	{
		msgLock->Acquire();
		IterList* msg;
		if(status=="TalkingToManager")
		{
			//Since the message had no arguments, I don't need to store it, just see if it is returned
			if(msgList->Find("Okay"))
			{
				ContinueOn();
				msgLock->Release();
				continue;
			}
			
			else if(msgList->Find("GoOnBreak"))
			{
				GoOnBreak();
				msgLock->Release();
				continue;
			}
			else if(msgList->Find("ComeOffBreak"))
			{
				ComeOffBreak();
				msgLock->Release();
				continue;
			}
		}
		else if(status=="WaitingForCust")
		{
			//I need to use the arguments, so I store the iterList
			msg=msgList->Find("HereAreTix");
			if(msg)
			{
				AcceptTickets(msg->Get(0),msg->Get(1),msg->GetInt(2));
				msgLock->Release();
				continue;
			}
		}
		else if(status=="SentToTheater")
		{
			msg=msgList->Find("ImGoingIn");
			if(msg)
			{
				SendInPerson();
				msgLock->Release();
				continue;
			}
		}
		else if(status=="OnBreak")
		{
			msg=msgList->Find("WhatIsStatus");
			if(msg)
			{
				TellStatus(msg->Get(0));
				msgLock->Release();
				continue;
			}
			msg=msgList->Find("IsYourLineEmpty");
			if(msg)
			{
				//printf("My line is empty\n");
				TellTechImIdle(msg->Get(0));
				msgLock->Release();
				continue;
			}
		}
		else if(status=="Idle")
		{
			msg=msgList->Find("WhatIsStatus");
			if(msg)
			{
				TellStatus(msg->Get(0));
				msgLock->Release();
				continue;
			}
			else if(CallNextInLine())
			{
				msgLock->Release();
				continue;
			}
			printf("My line is empty\n");
			msg=msgList->Find("IsYourLineEmpty");
			if(msg)
			{
				//printf("My line is empty\n");
				TellTechImIdle(msg->Get(0));
				msgLock->Release();
				continue;
			}
		}
		msgLock->Release();
		sleepCon->Wait(msgLock);
	} while(runScheduler);
	
	
}

//Gets the next customer in his ticket line.  Returns true if there is one, false otherwise
bool
TicketTaker::CallNextInLine()
{
	getCineplex()->entryLinesLock->Acquire();
	Customer* cust=(Customer*)myLine->customers->Pop(0);
	getCineplex()->entryLinesLock->Release();
	if(cust)
	{	
		printf("TicketTaker %s has a line length %i\n", myName, myLine->getSize());
		cust->SendMessage("GoToTicketTaker",1,this);
		status="WaitingForCust";
		return true;
	}
	status="Idle";
	printf("TicketTaker %s has no one in line. I am available for a customer.\n", myName);
	return false;
}

//Accepts the tickets from the customer and determines whether there is room for the group in the theater
void
TicketTaker::AcceptTickets(void* custPtr,void* ticketPtr, int size)
{
	Customer* cust=(Customer*)custPtr;
	getCineplex()->theater->seatingLock->Acquire();
	if(getCineplex()->theater->seatsFilled+size<=(NUM_ROWS*NUM_COLS)&&!getCineplex()->theater->movieStarted&& getCineplex()->letPeopleIn)
	{
		printf ("TicketTaker %s is allowing the group into the theater. The number of tickets taken is %i\n",myName,size);
		getCineplex()->theater->seatsFilled+=size;
		cust->SendMessage("GoOnIn",2,this,getCineplex()->theater);
		SizeOfGroupNotInTheater=size;
		status="SentToTheater";
	}
	else
	{
		printf("TicketTaker %s is not allowing the group into the theater. The number of taken tickets is %i and the group size is %i.\n",myName,getCineplex()->theater->seatsFilled,size);
		cust->SendMessage("GoToLobby",1,getCineplex()->lobby);
		status="Idle";	
	}
	getCineplex()->theater->seatingLock->Release();
}

//Recieves acknowledgement from people in the group that they are entering the theater
void
TicketTaker::SendInPerson()
{
	SizeOfGroupNotInTheater--;
	if(SizeOfGroupNotInTheater==0)
	{
		status="Idle";
	}
}

void
TicketTaker::TellTechImIdle(void* techPtr)
{
	MovieTech* tech=(MovieTech *)techPtr;
	tech->SendMessage("LineIsEmpty",0);
}

//Tells the manager how many people are in his line.  If he is on break, he returns -1
void
TicketTaker::TellStatus(void* mngrPtr)
{
	Manager* manager=(Manager *)mngrPtr;
	int lineLength;
	if(status=="OnBreak")
	{
		lineLength=-1;
		onBreak=true;
	}
	else
	{
		lineLength=myLine->getSize();
		onBreak=false;
	}
	manager->SendMessage("NumInLine",2,this,&lineLength);
	status="TalkingToManager";
}

//The manager doesn't tell the Taker to change break status, so he returns to the previous state
void
TicketTaker::ContinueOn()
{
	if(onBreak)
	{
			status="OnBreak";
	}
	else
	{
		status="Idle";
	}
}

//Takers goes on break
void
TicketTaker::GoOnBreak()
{
	printf("TicketTaker %s is going on break\n", myName);
	myLine->isOpen=0;
	myLine->Clear();
	status="OnBreak";
}

void
TicketTaker::ComeOffBreak()
{
	printf("TicketTaker %s is coming off break\n", myName);
	myLine->isOpen=1;
	status="Idle";
}
